/**
 * SeaJS v2.0.0 | seajs.org/LICENSE.md
 */
(function(global, undefined) {

// Avoid conflicting when `sea.js` is loaded multiple times
	var _seajs = global.__tk__seajs
	if (_seajs && _seajs.version) {
		return
	}

	var seajs = global.__tk__seajs = {
		// The current version of SeaJS being used
		version: "2.0.0"
	}


	/**
	 * util-lang.js - The minimal language enhancement
	 */

	function isType(type) {
		return function(obj) {
			return Object.prototype.toString.call(obj) === "[object " + type + "]"
		}
	}

	var isObject = isType("Object")
	var isString = isType("String")
	var isArray = Array.isArray || isType("Array")
	var isFunction = isType("Function")


	/**
	 * util-log.js - The tiny log function
	 */

// The safe wrapper for `console.xxx` functions
// log("message") ==> console.log("message")
// log("message", "warn") ==> console.warn("message")
	var log = seajs.log = function(msg, type) {

		global.console &&
			// Do NOT print `log(msg)` in non-debug mode
		(type || configData.debug) &&
			// Set the default value of type
		(console[type || (type = "log")]) &&
			// Call native method of console
		console[type](msg)
	}


	/**
	 * util-events.js - The minimal events support
	 */

	var eventsCache = seajs.events = {}

// Bind event
	seajs.on = function(event, callback) {
		if (!callback) return seajs

		var list = eventsCache[event] || (eventsCache[event] = [])
		list.push(callback)

		return seajs
	}

// Remove event. If `callback` is undefined, remove all callbacks for the
// event. If `event` and `callback` are both undefined, remove all callbacks
// for all events
	seajs.off = function(event, callback) {
		// Remove *all* events
		if (!(event || callback)) {
			seajs.events = eventsCache = {}
			return seajs
		}

		var list = eventsCache[event]
		if (list) {
			if (callback) {
				for (var i = list.length - 1; i >= 0; i--) {
					if (list[i] === callback) {
						list.splice(i, 1)
					}
				}
			}
			else {
				delete eventsCache[event]
			}
		}

		return seajs
	}

// Emit event, firing all bound callbacks. Callbacks are passed the same
// arguments as `emit` is, apart from the event name
	var emit = seajs.emit = function(event, data) {
		var list = eventsCache[event], fn

		if (list) {
			// Copy callback lists to prevent modification
			list = list.slice()

			// Execute event callbacks
			while ((fn = list.shift())) {
				fn(data)
			}
		}

		return seajs
	}


	/**
	 * util-path.js - The utilities for operating path such as id, uri
	 */

	var DIRNAME_RE = /[^?#]*\//

	var DOT_RE = /\/\.\//g
	var MULTIPLE_SLASH_RE = /([^:\/])\/\/+/g
	var DOUBLE_DOT_RE = /\/[^/]+\/\.\.\//g

	var URI_END_RE = /\?|\.(?:css|js)$|\/$/
	var HASH_END_RE = /#$/

// Extract the directory portion of a path
// dirname("a/b/c.js?t=123#xx/zz") ==> "a/b/"
// ref: http://jsperf.com/regex-vs-split/2
	function dirname(path) {
		return path.match(DIRNAME_RE)[0]
	}

// Canonicalize a path
// realpath("http://test.com/a//./b/../c") ==> "http://test.com/a/c"
	function realpath(path) {
		// /a/b/./c/./d ==> /a/b/c/d
		path = path.replace(DOT_RE, "/")

		// "file:///a//b/c"  ==> "file:///a/b/c"
		// "http://a//b/c"   ==> "http://a/b/c"
		// "https://a//b/c"  ==> "https://a/b/c"
		// "/a/b//"          ==> "/a/b/"
		path = path.replace(MULTIPLE_SLASH_RE, "$1\/")

		// a/b/c/../../d  ==>  a/b/../d  ==>  a/d
		while (path.match(DOUBLE_DOT_RE)) {
			path = path.replace(DOUBLE_DOT_RE, "/")
		}

		return path
	}

// Normalize an uri
// normalize("path/to/a") ==> "path/to/a.js"
	function normalize(uri) {
		// Call realpath() before adding extension, so that most of uris will
		// contains no `.` and will just return in realpath() call
		uri = realpath(uri)

		// Add the default `.js` extension except that the uri ends with `#`
		if (HASH_END_RE.test(uri)) {
			uri = uri.slice(0, -1)
		}
		else if (!URI_END_RE.test(uri)) {
			uri += ".js"
		}

		// issue #256: fix `:80` bug in IE
		return uri.replace(":80/", "/")
	}


	var PATHS_RE = /^([^/:]+)(\/.+)$/
	var VARS_RE = /{([^{]+)}/g

	function parseAlias(id) {
		var alias = configData.alias
		return alias && isString(alias[id]) ? alias[id] : id
	}

	function parsePaths(id) {
		var paths = configData.paths
		var m

		if (paths && (m = id.match(PATHS_RE)) && isString(paths[m[1]])) {
			id = paths[m[1]] + m[2]
		}

		return id
	}

	function parseVars(id) {
		var vars = configData.vars

		if (vars && id.indexOf("{") > -1) {
			id = id.replace(VARS_RE, function(m, key) {
				return isString(vars[key]) ? vars[key] : m
			})
		}

		return id
	}

	function parseMap(uri) {
		var map = configData.map
		var ret = uri

		if (map) {
			for (var i = 0; i < map.length; i++) {
				var rule = map[i]

				ret = isFunction(rule) ?
					(rule(uri) || uri) :
					uri.replace(rule[0], rule[1])

				// Only apply the first matched rule
				if (ret !== uri) break
			}
		}

		return ret
	}


	var ABSOLUTE_RE = /:\//
	var RELATIVE_RE = /^\./
	var ROOT_RE = /^\//

	function isAbsolute(id) {
		return ABSOLUTE_RE.test(id)
	}

	function isRelative(id) {
		return RELATIVE_RE.test(id)
	}

	function isRoot(id) {
		return ROOT_RE.test(id)
	}


	var ROOT_DIR_RE = /^.*?\/\/.*?\//

	function addBase(id, refUri) {
		var ret

		if (isAbsolute(id)) {
			ret = id
		}
		else if (isRelative(id)) {
			ret = dirname(refUri || cwd) + id
		}
		else if (isRoot(id)) {
			ret = (cwd.match(ROOT_DIR_RE) || ["/"])[0] + id.substring(1)
		}
		// top-level id
		else {
			ret = configData.base + id
		}

		return ret
	}

	function id2Uri(id, refUri) {
		if (!id) return ""

		id = parseAlias(id)
		id = parsePaths(id)
		id = parseVars(id)
		id = addBase(id, refUri)
		id = normalize(id)
		id = parseMap(id)

		return id
	}


	var doc = document
	var loc = location
	var cwd = dirname(loc.href)
	var scripts = doc.getElementsByTagName("script")

// Recommend to add `seajs-node` id for the `sea.js` script element
	var loaderScript = doc.getElementById("__tk__seajsnode") ||
		scripts[scripts.length - 1]

// When `sea.js` is inline, set loaderDir to current working directory
	var loaderDir = dirname(getScriptAbsoluteSrc(loaderScript)) || cwd

	function getScriptAbsoluteSrc(node) {
		return node.hasAttribute ? // non-IE6/7
			node.src :
			// see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
			node.getAttribute("src", 4)
	}

// Get/set current working directory
	seajs.cwd = function(val) {
		return val ? (cwd = realpath(val + "/")) : cwd
	}


	/**
	 * util-request.js - The utilities for requesting script and style files
	 * ref: tests/research/load-js-css/test.html
	 */

	var head = doc.getElementsByTagName("head")[0] || doc.documentElement
	var baseElement = head.getElementsByTagName("base")[0]

	var IS_CSS_RE = /\.css(?:\?|$)/i
	var READY_STATE_RE = /^(?:loaded|complete|undefined)$/

	var currentlyAddingScript
	var interactiveScript

// `onload` event is supported in WebKit < 535.23 and Firefox < 9.0
// ref:
//  - https://bugs.webkit.org/show_activity.cgi?id=38995
//  - https://bugzilla.mozilla.org/show_bug.cgi?id=185236
//  - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events
	var isOldWebKit = (navigator.userAgent
			.replace(/.*AppleWebKit\/(\d+)\..*/, "$1")) * 1 < 536


	function request(url, callback, charset) {
		var isCSS = IS_CSS_RE.test(url)
		var node = doc.createElement(isCSS ? "link" : "script")

		if (charset) {
			var cs = isFunction(charset) ? charset(url) : charset
			if (cs) {
				node.charset = cs
			}
		}

		addOnload(node, callback, isCSS)

		if (isCSS) {
			node.rel = "stylesheet"
			node.href = url
		}
		else {
			node.async = true
			node.src = url
		}

		// For some cache cases in IE 6-8, the script executes IMMEDIATELY after
		// the end of the insert execution, so use `currentlyAddingScript` to
		// hold current node, for deriving url in `define` call
		currentlyAddingScript = node

		// ref: #185 & http://dev.jquery.com/ticket/2709
		baseElement ?
			head.insertBefore(node, baseElement) :
			head.appendChild(node)

		currentlyAddingScript = undefined
	}

	function addOnload(node, callback, isCSS) {
		var missingOnload = isCSS && (isOldWebKit || !("onload" in node))

		// for Old WebKit and Old Firefox
		if (missingOnload) {
			setTimeout(function() {
				pollCss(node, callback)
			}, 1) // Begin after node insertion
			return
		}

		node.onload = node.onerror = node.onreadystatechange = function() {
			if (READY_STATE_RE.test(node.readyState)) {

				// Ensure only run once and handle memory leak in IE
				node.onload = node.onerror = node.onreadystatechange = null

				// Remove the script to reduce memory leak
				if (!isCSS && !configData.debug) {
					head.removeChild(node)
				}

				// Dereference the node
				node = undefined

				callback()
			}
		}
	}

	function pollCss(node, callback) {
		var sheet = node.sheet
		var isLoaded

		// for WebKit < 536
		if (isOldWebKit) {
			if (sheet) {
				isLoaded = true
			}
		}
		// for Firefox < 9.0
		else if (sheet) {
			try {
				if (sheet.cssRules) {
					isLoaded = true
				}
			} catch (ex) {
				// The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
				// to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
				// in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
				if (ex.name === "NS_ERROR_DOM_SECURITY_ERR") {
					isLoaded = true
				}
			}
		}

		setTimeout(function() {
			if (isLoaded) {
				// Place callback here to give time for style rendering
				callback()
			}
			else {
				pollCss(node, callback)
			}
		}, 20)
	}

	function getCurrentScript() {
		if (currentlyAddingScript) {
			return currentlyAddingScript
		}

		// For IE6-9 browsers, the script onload event may not fire right
		// after the the script is evaluated. Kris Zyp found that it
		// could query the script nodes and the one that is in "interactive"
		// mode indicates the current script
		// ref: http://goo.gl/JHfFW
		if (interactiveScript && interactiveScript.readyState === "interactive") {
			return interactiveScript
		}

		var scripts = head.getElementsByTagName("script")

		for (var i = scripts.length - 1; i >= 0; i--) {
			var script = scripts[i]
			if (script.readyState === "interactive") {
				interactiveScript = script
				return interactiveScript
			}
		}
	}


	/**
	 * util-deps.js - The parser for dependencies
	 * ref: tests/research/parse-dependencies/test.html
	 */

	var REQUIRE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g
	var SLASH_RE = /\\\\/g

	function parseDependencies(code) {
		var ret = []

		code.replace(SLASH_RE, "")
			.replace(REQUIRE_RE, function(m, m1, m2) {
				if (m2) {
					ret.push(m2)
				}
			})

		return ret
	}


	/**
	 * module.js - The core of module loader
	 */

	var cachedModules = seajs.cache = {}
	var anonymousModuleData

	var fetchingList = {}
	var fetchedList = {}
	var callbackList = {}
	var waitingsList = {}

// 1 - The module file is being fetched now
// 2 - The module data has been saved to cachedModules
// 3 - The module and all its dependencies are ready to execute
// 4 - The module is being executed
// 5 - The module is executed and `module.exports` is available
	var STATUS_FETCHING = 1
	var STATUS_SAVED = 2
	var STATUS_LOADED = 3
	var STATUS_EXECUTING = 4
	var STATUS_EXECUTED = 5


	function Module(uri) {
		this.uri = uri
		this.dependencies = []
		this.exports = null
		this.status = 0
	}

	function resolve(ids, refUri) {
		if (isArray(ids)) {
			var ret = []
			for (var i = 0; i < ids.length; i++) {
				ret[i] = resolve(ids[i], refUri)
			}
			return ret
		}

		// Emit `resolve` event for plugins such as plugin-text
		var data = { id: ids, refUri: refUri }
		emit("resolve", data)

		return data.uri || id2Uri(data.id, refUri)
	}

	function use(uris, callback) {
		isArray(uris) || (uris = [uris])

		load(uris, function() {
			var exports = []

			for (var i = 0; i < uris.length; i++) {
				exports[i] = getExports(cachedModules[uris[i]])
			}

			if (callback) {
				callback.apply(global, exports)
			}
		})
	}

	function load(uris, callback) {
		var unloadedUris = getUnloadedUris(uris)

		if (unloadedUris.length === 0) {
			callback()
			return
		}

		// Emit `load` event for plugins such as plugin-combo
		emit("load", unloadedUris)

		var len = unloadedUris.length
		var remain = len

		for (var i = 0; i < len; i++) {
			(function(uri) {
				var mod = cachedModules[uri]

				if (mod.dependencies.length) {
					loadWaitings(function(circular) {
						mod.status < STATUS_SAVED ? fetch(uri, cb) : cb()
						function cb() {
							done(circular)
						}
					})
				}
				else {
					mod.status < STATUS_SAVED ?
						fetch(uri, loadWaitings) : done()
				}

				function loadWaitings(cb) {
					cb || (cb = done)

					var waitings = getUnloadedUris(mod.dependencies)
					if (waitings.length === 0) {
						cb()
					}
					// Break circular waiting callbacks
					else if (isCircularWaiting(mod)) {
						printCircularLog(circularStack)
						circularStack.length = 0
						cb(true)
					}
					// Load all unloaded dependencies
					else {
						waitingsList[uri] = waitings
						load(waitings, cb)
					}
				}

				function done(circular) {
					if (!circular && mod.status < STATUS_LOADED) {
						mod.status = STATUS_LOADED
					}

					if (--remain === 0) {
						callback()
					}
				}

			})(unloadedUris[i])
		}
	}

	function fetch(uri, callback) {
		cachedModules[uri].status = STATUS_FETCHING

		// Emit `fetch` event for plugins such as plugin-combo
		var data = { uri: uri }
		emit("fetch", data)
		var requestUri = data.requestUri || uri

		if (fetchedList[requestUri]) {
			callback()
			return
		}

		if (fetchingList[requestUri]) {
			callbackList[requestUri].push(callback)
			return
		}

		fetchingList[requestUri] = true
		callbackList[requestUri] = [callback]

		// Emit `request` event for plugins such as plugin-text
		var charset = configData.charset
		emit("request", data = {
			uri: uri,
			requestUri: requestUri,
			callback: onRequested,
			charset: charset
		})

		if (!data.requested) {
			request(data.requestUri, onRequested, charset)
		}

		function onRequested() {
			delete fetchingList[requestUri]
			fetchedList[requestUri] = true

			// Save meta data of anonymous module
			if (anonymousModuleData) {
				save(uri, anonymousModuleData)
				anonymousModuleData = undefined
			}

			// Call callbacks
			var fn, fns = callbackList[requestUri]
			delete callbackList[requestUri]
			while ((fn = fns.shift())) fn()
		}
	}

	function __tk__define(id, deps, factory) {
		// define(factory)
		if (arguments.length === 1) {
			factory = id
			id = undefined
		}

		// Parse dependencies according to the module factory code
		if (!isArray(deps) && isFunction(factory)) {
			deps = parseDependencies(factory.toString())
		}

		var data = { id: id, uri: resolve(id), deps: deps, factory: factory }

		// Try to derive uri in IE6-9 for anonymous modules
		if (!data.uri && doc.attachEvent) {
			var script = getCurrentScript()

			if (script) {
				data.uri = script.src
			}
			else {
				log("Failed to derive: " + factory)

				// NOTE: If the id-deriving methods above is failed, then falls back
				// to use onload event to get the uri
			}
		}

		// Emit `define` event, used in plugin-nocache, seajs node version etc
		emit("define", data)

		data.uri ? save(data.uri, data) :
			// Save information for "saving" work in the script onload event
			anonymousModuleData = data
	}

	function save(uri, meta) {
		var mod = getModule(uri)

		// Do NOT override already saved modules
		if (mod.status < STATUS_SAVED) {
			// Let the id of anonymous module equal to its uri
			mod.id = meta.id || uri

			mod.dependencies = resolve(meta.deps || [], uri)
			mod.factory = meta.factory

			if (mod.factory !== undefined) {
				mod.status = STATUS_SAVED
			}
		}
	}

	function exec(mod) {
		// Return `null` when `mod` is invalid
		if (!mod) {
			return null
		}

		// When module is executed, DO NOT execute it again. When module
		// is being executed, just return `module.exports` too, for avoiding
		// circularly calling
		if (mod.status >= STATUS_EXECUTING) {
			return mod.exports
		}

		mod.status = STATUS_EXECUTING


		function resolveInThisContext(id) {
			return resolve(id, mod.uri)
		}

		function require(id) {
			return getExports(cachedModules[resolveInThisContext(id)])
		}

		require.resolve = resolveInThisContext

		require.async = function(ids, callback) {
			use(resolveInThisContext(ids), callback)
			return require
		}


		var factory = mod.factory

		var exports = isFunction(factory) ?
			factory(require, mod.exports = {}, mod) :
			factory

		mod.exports = exports === undefined ? mod.exports : exports
		mod.status = STATUS_EXECUTED

		return mod.exports
	}

	Module.prototype.destroy = function() {
		delete cachedModules[this.uri]
		delete fetchedList[this.uri]
	}


// Helpers

	function getModule(uri) {
		return cachedModules[uri] ||
			(cachedModules[uri] = new Module(uri))
	}

	function getUnloadedUris(uris) {
		var ret = []

		for (var i = 0; i < uris.length; i++) {
			var uri = uris[i]
			if (uri && getModule(uri).status < STATUS_LOADED) {
				ret.push(uri)
			}
		}

		return ret
	}

	function getExports(mod) {
		var exports = exec(mod)
		if (exports === null && (!mod || !IS_CSS_RE.test(mod.uri))) {
			emit("error", mod)
		}
		return exports
	}

	var circularStack = []

	function isCircularWaiting(mod) {
		var waitings = waitingsList[mod.uri] || []
		if (waitings.length === 0) {
			return false
		}

		circularStack.push(mod.uri)
		if (isOverlap(waitings, circularStack)) {
			cutWaitings(waitings)
			return true
		}

		for (var i = 0; i < waitings.length; i++) {
			if (isCircularWaiting(cachedModules[waitings[i]])) {
				return true
			}
		}

		circularStack.pop()
		return false
	}

	function isOverlap(arrA, arrB) {
		for (var i = 0; i < arrA.length; i++) {
			for (var j = 0; j < arrB.length; j++) {
				if (arrB[j] === arrA[i]) {
					return true
				}
			}
		}
		return false
	}

	function cutWaitings(waitings) {
		var uri = circularStack[0]

		for (var i = waitings.length - 1; i >= 0; i--) {
			if (waitings[i] === uri) {
				waitings.splice(i, 1)
				break
			}
		}
	}

	function printCircularLog(stack) {
		stack.push(stack[0])
		log("Circular dependencies: " + stack.join(" -> "))
	}

	function preload(callback) {
		var preloadMods = configData.preload
		var len = preloadMods.length

		if (len) {
			use(resolve(preloadMods), function() {
				// Remove the loaded preload modules
				preloadMods.splice(0, len)

				// Allow preload modules to add new preload modules
				preload(callback)
			})
		}
		else {
			callback()
		}
	}


// Public API

	seajs.use = function(ids, callback) {
		// Load preload modules before all other modules
		preload(function() {
			use(resolve(ids), callback)
		})
		return seajs
	}

	Module.load = use
	seajs.resolve = id2Uri
	global.__tk__define = __tk__define

	seajs.require = function(id) {
		return (cachedModules[id2Uri(id)] || {}).exports
	}


	/**
	 * config.js - The configuration for the loader
	 */

	var configData = config.data = {
		// The root path to use for id2uri parsing
		base: (function() {
			var ret = loaderDir

			// If loaderUri is `http://test.com/libs/seajs/[seajs/1.2.3/]sea.js`, the
			// baseUri should be `http://test.com/libs/`
			var m = ret.match(/^(.+?\/)(?:__tk__seajs\/)+(?:\d[^/]+\/)?$/)
			if (m) {
				ret = m[1]
			}

			return ret
		})(),

		// The charset for requesting files
		charset: "utf-8",

		// Modules that are needed to load before all other modules
		preload: []

		// debug - Debug mode. The default value is false
		// alias - An object containing shorthands of module id
		// paths - An object containing path shorthands in module id
		// vars - The {xxx} variables in module id
		// map - An array containing rules to map module uri
		// plugins - An array containing needed plugins
	}

	function config(data) {
		for (var key in data) {
			var curr = data[key]

			// Convert plugins to preload config
			if (curr && key === "plugins") {
				key = "preload"
				curr = plugin2preload(curr)
			}

			var prev = configData[key]

			// Merge object config such as alias, vars
			if (prev && isObject(prev)) {
				for (var k in curr) {
					prev[k] = curr[k]
				}
			}
			else {
				// Concat array config such as map, preload
				if (isArray(prev)) {
					curr = prev.concat(curr)
				}
				// Make sure that `configData.base` is an absolute directory
				else if (key === "base") {
					curr = normalize(addBase(curr + "/"))
				}

				// Set config
				configData[key] = curr
			}
		}

		emit("config", data)
		return seajs
	}

	seajs.config = config

	function plugin2preload(arr) {
		var ret = [], name

		while ((name = arr.shift())) {
			ret.push(loaderDir + "plugin-" + name)
		}
		return ret
	}


	/**
	 * bootstrap.js - Initialize the plugins and load the entry module
	 */

	config({
		// Get initial plugins
		plugins: (function() {
			var ret

			// Convert `seajs-xxx` to `seajs-xxx=1`
			// NOTE: use `seajs-xxx=1` flag in url or cookie to enable `plugin-xxx`
			var str = loc.search.replace(/(__tk__seajs-\w+)(&|$)/g, "$1=1$2")

			// Add cookie string
			str += " " + doc.cookie

			// Exclude seajs-xxx=0
			str.replace(/__tk__seajs-(\w+)=1/g, function(m, name) {
				(ret || (ret = [])).push(name)
			})

			return ret
		})()
	})

	var dataConfig = loaderScript.getAttribute("data-config")
	var dataMain = loaderScript.getAttribute("data-main")

// Add data-config to preload modules
	if (dataConfig) {
		configData.preload.push(dataConfig)
	}

	if (dataMain) {
		seajs.use(dataMain)
	}

// Enable to load `sea.js` self asynchronously
	if (_seajs && _seajs.args) {
		var methods = ["define", "config", "use"]
		var args = _seajs.args
		for (var g = 0; g < args.length; g += 2) {
			seajs[methods[args[g]]].apply(seajs, args[g + 1])
		}
	}

	/*
	 ;(function(m, o, d, u, l, a, r) {
	 if(m[o]) return
	 function f(n) { return function() { r.push(n, arguments); return a } }
	 m[o] = a = { args: (r = []), config: f(1), use: f(2) }
	 m.define = f(0)
	 u = d.createElement("script")
	 u.id = o + "node"
	 u.async = true
	 u.src = "path/to/sea.js"
	 l = d.getElementsByTagName("head")[0]
	 l.appendChild(u)
	 })(window, "seajs", document);
	 */

})(this);
